FORM BANNER
FORM BANNER

Librerías necesarias

library(imputeTS)
library(lubridate)
library(xts)
library(zoo)
library(tseries)
library(stats)
library(forecast)
library(astsa)
library(corrplot)
library(wordcloud)
library(tidytext)
library(AER)
library(vars)
library(dynlm)
library(mFilter)
library(TSstudio)
library(tidyverse)
library(sarima)
library(readr)
library(readxl)
library(patchwork)
library(heatmaply)
library(dplyr)
library(ggplot2)
library(psych)
library(tidyr)
library(readtext)
library(syuzhet)
library(RColorBrewer)
library(tm)
library(caret)
library(MASS)
library(rpart)
library(rpart.plot)
library(party)
library(gmodels)
library(knitr)
library(cluster)    
library(e1071)
library(janeaustenr)
library(pROC)
library(ISLR)
library(gridExtra)
library(car)
library(DataExplorer)
library(randomForest)
library(class)
library(factoextra)
library(purrr)
library(reshape2)
library(tmap)
library(sf)
library(zoo)

Bases de Datos Necesarias

setwd("../databases")

form_satisfaccion <- read_excel("form/Encuesta_Datos_FORM_Fall2023.xlsx")
form_bajas = read_xlsx("form/temporary/BDD_FORM_BAJAS-2023.xlsx")
cp_nl <- st_read("geo_reference__nl/CP_NL")
## Reading layer `CP_19NL_v10' from data source 
##   `/Users/daviddrums180/Tec/Case_Study_Form/databases/geo_reference__nl/CP_NL' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 1338 features and 1 field
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 2578831 ymin: 1238179 xmax: 2858006 ymax: 1749577
## Projected CRS: Lambert_Conformal_Conic
exp_vehiculos = read_csv("industry_autos_mx/exportacion_vehiculos_mx.csv")
ventas_vehiculos = read_csv("industry_autos_mx/mx_venta_vehiculos.csv")
exp_vehiculos_total = read_excel("industry_autos_mx/exportacion_vehiculos.xlsx")

Transformaciones Necesarias

FORM Bajas

# Limpieza de Fecha
form_bajas$`Fecha de Nacimiento` <- as.Date(form_bajas$`Fecha de Nacimiento` - ifelse(form_bajas$`Fecha de Nacimiento` > 60, 1, 0), origin="1899-12-30")
form_bajas$`Fecha de Alta` <- as.Date(form_bajas$`Fecha de Alta` - ifelse(form_bajas$`Fecha de Alta` > 60, 1, 0), origin="1899-12-30")
form_bajas$`Primer Mes` <- as.Date(form_bajas$`Primer Mes` - ifelse(form_bajas$`Primer Mes` > 60, 1, 0), origin="1899-12-30")
form_bajas$`Cuarto Mes` <- as.Date(form_bajas$`Cuarto Mes` - ifelse(form_bajas$`Cuarto Mes` > 60, 1, 0), origin="1899-12-30")
form_bajas$`Fecha de Baja` <- as.Date(form_bajas$`Fecha de Baja` - ifelse(form_bajas$`Fecha de Baja` > 60, 1, 0), origin="1899-12-30")

# Calcular la antigüedad como la diferencia entre Fecha de Baja y Fecha de Alta
form_bajas$Antiguedad <- abs(as.numeric(form_bajas$`Fecha de Baja` - form_bajas$`Fecha de Alta`, units = "days"))

head(form_bajas)
## # A tibble: 6 × 27
##     No. Apellidos      Nombre `Fecha de Nacimiento` Género RFC   `Fecha de Alta`
##   <dbl> <chr>          <chr>  <date>                <chr>  <chr> <date>         
## 1     1 Perez Chavarr… Yessi… 1985-02-12            Femen… PECY… 2022-09-04     
## 2     1 Pecina Aleman  Blanc… 1966-05-24            Femen… PEAB… 2022-10-05     
## 3     1 Suarez Romo    Julio… 1969-06-26            Mascu… SURJ… 2017-11-30     
## 4     1 Ortiz De La T… Fermi… 1966-07-06            Femen… OITF… 2022-05-31     
## 5     1 Gallegos Manz… Veron… 1973-11-27            Femen… GAMV… 2022-10-21     
## 6     1 Guzman Reyes   Carlo… 2002-11-24            Mascu… GURC… 2023-01-05     
## # ℹ 20 more variables: `Primer Mes` <date>, `Cuarto Mes` <date>,
## #   `Fecha de Baja` <date>, `Motivo de Baja` <chr>, Puesto <chr>, Dpto <chr>,
## #   Imss <chr>, SD <chr>, `Factor de Crédito Infonavit` <chr>,
## #   `No. De Crédito Infonavit` <chr>, CURP <chr>, Calle <chr>, Número <chr>,
## #   Colonia <chr>, Municipio <chr>, Estado <chr>, CP <dbl>,
## #   `Estado Civil` <chr>, `Número de Télefono` <dbl>, Antiguedad <dbl>

FORM Bajas Espacial

form_bajas$CP <- as.character(form_bajas$CP)
cp_nl$d_codigo <- as.character(cp_nl$d_codigo)

stats_por_cp <- form_bajas %>%
  group_by(CP) %>%
  summarize(
    Conteo_Empleados = n(),
    Mediana_Antiguedad = median(Antiguedad, na.rm = TRUE),
    .groups = 'drop'
  )

form_bajas_espacial <- cp_nl %>%
  left_join(stats_por_cp, by = c("d_codigo" = "CP"))

# Reemplazar NA con 0 en las columnas de interés
form_bajas_espacial$Conteo_Empleados[is.na(form_bajas_espacial$Conteo_Empleados)] <- 0
form_bajas_espacial$Mediana_Antiguedad[is.na(form_bajas_espacial$Mediana_Antiguedad)] <- 0

head(form_bajas_espacial)
## Simple feature collection with 6 features and 3 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 2661927 ymin: 1481451 xmax: 2725620 ymax: 1529424
## Projected CRS: Lambert_Conformal_Conic
##   d_codigo Conteo_Empleados Mediana_Antiguedad                       geometry
## 1    66063                0                  0 MULTIPOLYGON (((2662011 152...
## 2    67495                0                  0 MULTIPOLYGON (((2716586 149...
## 3    67467                0                  0 MULTIPOLYGON (((2707035 148...
## 4    67494                0                  0 MULTIPOLYGON (((2718640 149...
## 5    67475                0                  0 MULTIPOLYGON (((2707164 151...
## 6    64010                0                  0 MULTIPOLYGON (((2670043 151...

FORM Satisfacción

# Lista de columnas específicas a transformar
columnas_a_transformar <- c("salario_bueno", "prestaciones_bueno", "jornada_no_excesiva", 
                            "ofrecimiento_herramientas", "no_molestia_temperatura", "estres_bajo", 
                            "facilidad_transporte", "zona_trabajo_comoda", "permanencia_form_futuro")

# Ajustar la función para manejar el typo y valores inesperados
codificar_respuestas <- function(respuesta) {
  # Corregir posibles typos
  respuesta <- gsub("Totalmende en desacuerdo", "Totalmente en desacuerdo", respuesta)
  
  # Usar switch para asignar valores numéricos
  switch(respuesta,
         "Totalmente en desacuerdo" = 1,
         "Medianamente en desacuerdo" = 2,
         "Ni de acuerdo ni en desacuerdo" = 3,
         "Medianamente de acuerdo" = 4,
         "Totalmente de acuerdo" = 5,
         NA) # Devolver NA para cualquier respuesta no reconocida
}

# Aplicar la codificación solo a las columnas seleccionadas, asegurando que todas las transformaciones son numéricas
form_satisfaccion[columnas_a_transformar] <- lapply(form_satisfaccion[columnas_a_transformar], function(x) as.numeric(sapply(x, codificar_respuestas)))

head(form_satisfaccion)
## # A tibble: 6 × 22
##   encuesta puesto      antiguedad razon_entrada salario_bueno prestaciones_bueno
##      <dbl> <chr>            <dbl> <chr>                 <dbl>              <dbl>
## 1        1 administra…          9 salario                   5                  4
## 2        2 costurera           36 otro                      4                  4
## 3        3 ayudante g…          4 ubicacion em…             2                  1
## 4        4 ayudante g…          2 ubicacion em…             5                  4
## 5        5 ayudante g…          1 ubicacion em…             3                  1
## 6        6 ayudante g…         36 razones pers…             4                  5
## # ℹ 16 more variables: jornada_no_excesiva <dbl>,
## #   ofrecimiento_herramientas <dbl>, no_molestia_temperatura <dbl>,
## #   estres_bajo <dbl>, facilidad_transporte <dbl>, zona_trabajo_comoda <dbl>,
## #   permanencia_form_futuro <dbl>, sufrido_situaciones_conflicto <chr>,
## #   molestias_puesto <chr>, sentimiento_form <chr>, edad <chr>, genero <chr>,
## #   estado_civil <chr>, municipio <chr>, nivel_escolar <chr>,
## #   personas_dependientes <dbl>

Exportación de Vehículos México

exp_vehiculos$Fecha <- as.yearqtr(exp_vehiculos$quarter, format = "%Y-Q%q")

Venta de Vehículos México

ventas_vehiculos$Fecha <- as.yearqtr(paste(ventas_vehiculos$ANIO, ventas_vehiculos$ID_MES, "1"), format = "%Y %m %d")

Preguntas de Análisis

Situación Problema 1: Rotación de Personal

¿Qué está causando la rotación de personal tan alta?

# Generar los datos para la variable 'no_molestia_temperatura'
datos_temperatura <- form_satisfaccion %>%
  group_by(no_molestia_temperatura) %>%
  summarise(Count = n()) %>%
  mutate(Percentage = Count / sum(Count) * 100)

# Paleta de colores personalizada: rojo, dorado, verde
colores_personalizados <- c("#d7191c", "#fdae61", "#ffffbf", "#a6d96a", "#1a9641")

# Gráfico de barras para 'no_molestia_temperatura'
ggplot(datos_temperatura, aes(x = factor(no_molestia_temperatura, levels = c(1, 2, 3, 4, 5)), y = Percentage, fill = as.factor(no_molestia_temperatura))) +
  geom_bar(stat = "identity") +
  scale_fill_manual(values = colores_personalizados) +
  scale_x_discrete(labels = c("Totalmente\nen desacuerdo", "Medianamente\nen desacuerdo", "Ni de acuerdo\nni en desacuerdo", "Medianamente\nde acuerdo", "Totalmente\nde acuerdo")) +
  xlab("Nivel de Acuerdo con la Temperatura") +
  ylab("Porcentaje") +
  ggtitle("Distribución de Respuestas para 'No Molestia por la Temperatura'") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 0, hjust = 0.5, size = 8), 
        legend.title = element_blank(),
        legend.position = "bottom")

variables_categoricas <- c("salario_bueno", "prestaciones_bueno", "jornada_no_excesiva", 
                           "ofrecimiento_herramientas", "no_molestia_temperatura", "estres_bajo", 
                           "facilidad_transporte", "zona_trabajo_comoda", "permanencia_form_futuro")

# Calculamos los porcentajes por categoría para cada variable
porcentajes_categorias <- form_satisfaccion %>%
  select(all_of(variables_categoricas)) %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor") %>%
  group_by(Variable) %>%
  count(Valor) %>%
  mutate(Porcentaje = n / sum(n) * 100) %>%
  ungroup() %>%
  select(-n)  # Si no necesitas la columna de conteos, puedes omitirla.

# Convertir a un formato ancho específico para cada variable y valor
porcentajes_ancho <- porcentajes_categorias %>%
  pivot_wider(names_from = Valor, values_from = Porcentaje, names_prefix = "Categoria_") %>%
  select(Variable, starts_with("Categoria_")) %>%
  arrange(Variable)

# Modificamos 'porcentajes_ancho' para formatear los porcentajes
porcentajes_ancho <- porcentajes_ancho %>%
  mutate(across(starts_with("Categoria_"), ~sprintf("%.1f%%", .)))

# Ver los resultados
print(porcentajes_ancho)
## # A tibble: 9 × 6
##   Variable           Categoria_1 Categoria_2 Categoria_3 Categoria_4 Categoria_5
##   <chr>              <chr>       <chr>       <chr>       <chr>       <chr>      
## 1 estres_bajo        12.3%       8.5%        18.9%       19.8%       40.6%      
## 2 facilidad_transpo… 14.2%       8.5%        1.9%        13.2%       62.3%      
## 3 jornada_no_excesi… 7.5%        5.7%        9.4%        17.9%       59.4%      
## 4 no_molestia_tempe… 33.0%       6.6%        11.3%       13.2%       35.8%      
## 5 ofrecimiento_herr… 18.9%       1.9%        8.5%        14.2%       56.6%      
## 6 permanencia_form_… 8.5%        4.7%        14.2%       19.8%       52.8%      
## 7 prestaciones_bueno 19.8%       17.9%       9.4%        20.8%       32.1%      
## 8 salario_bueno      10.4%       10.4%       7.5%        33.0%       38.7%      
## 9 zona_trabajo_como… 7.5%        4.7%        3.8%        17.0%       67.0%

¿Existe algún momento del año donde se den más bajas dentro de la empresa?

# Agregar una columna que indique el mes de la fecha de baja
form_bajas$Mes <- month(form_bajas$`Fecha de Baja`)
form_bajas$Año <- year(form_bajas$`Fecha de Baja`)

# Agrupar por mes y género, luego contar las bajas
bajas_por_mes_genero <- form_bajas %>%
  group_by(Mes, Género) %>%
  summarise(Conteo = n(), .groups = 'drop') %>%
  arrange(Mes)

# Graficar las bajas por mes para cada género
ggplot(bajas_por_mes_genero, aes(x = Mes, y = Conteo, group = Género, color = Género)) +
  geom_line() +
  scale_x_continuous(breaks = 1:12, labels = month.name) +
  theme_minimal() +
  labs(title = "Bajas por Mes Dividido por Género",
       x = "Mes",
       y = "Número de Bajas",
       color = "Género")

# Configurar tmap para que intente reparar automáticamente los polígonos inválidos
tmap_options(check.and.fix = TRUE)

# Calcular los breaks para Conteo de Empleados excluyendo los ceros
conteo_empleados_values <- form_bajas_espacial$Conteo_Empleados[form_bajas_espacial$Conteo_Empleados > 0]
breaks_conteo <- c(-Inf, quantile(conteo_empleados_values, probs = seq(0, 1, by = 0.25), na.rm = TRUE))

# Calcular los breaks para Mediana de Antigüedad excluyendo los ceros
antiguedad_values <- form_bajas_espacial$Mediana_Antiguedad[form_bajas_espacial$Mediana_Antiguedad > 0]
breaks_antiguedad <- c(-Inf, quantile(antiguedad_values, probs = seq(0, 1, by = 0.25), na.rm = TRUE))

# Mapa para Conteo de Empleados
conteo_empleados_map <- tm_shape(form_bajas_espacial) + 
  tm_fill("Conteo_Empleados", palette = "Blues", style = "fixed", breaks = breaks_conteo, 
          title = "Conteo de Empleados", na.color = "white") +
  tm_borders() +
  tm_layout(frame = FALSE, legend.position = c("left", "bottom"))

# Mapa para Mediana de Antigüedad
mediana_antiguedad_map <- tm_shape(form_bajas_espacial) + 
  tm_fill("Mediana_Antiguedad", palette = "BuPu", style = "fixed", breaks = breaks_antiguedad, 
          title = "Mediana de Antigüedad", na.color = "white") +
  tm_borders() +
  tm_layout(frame = FALSE, legend.position = c("right", "bottom"))

# Configurar opciones de tmap para la visualización de los mapas
tmap_mode("view")

# Mostrar mapas lado a lado
tmap_arrange(conteo_empleados_map, mediana_antiguedad_map, nrow = 1)

¿Cómo se siente la gente dentro de su trabajo actual?

# Función ajustada para preprocesar texto
preparar_texto <- function(datos, columna) {
  datos %>%
    select(!!sym(columna)) %>%
    mutate(across(everything(), as.character)) %>%
    mutate(across(everything(), ~ gsub("\\d+", "", .))) %>%
    unnest_tokens(word, !!sym(columna))
}

# Función ajustada para análisis de sentimiento y wordcloud con colores personalizados
analizar_y_wordcloud <- function(datos, columna) {
  texto_preparado <- preparar_texto(datos, columna)
  
  # Obtener sentimiento
  emociones_df <- texto_preparado %>%
    inner_join(get_sentiments("nrc"), by = "word") %>%
    count(sentiment, sort = TRUE) %>%
    print()
  
  # Filtrar palabras para el wordcloud (aparición mínima: 3 veces)
  palabras_frecuentes <- texto_preparado %>%
    count(word, sort = TRUE) %>%
    filter(n >= 3)
  
  # Definir paleta de colores de gris claro a naranja
  colores_gris_naranja <- colorRampPalette(c("darkgrey", "orange"))(100)
  
  # Generar wordcloud con la paleta personalizada
  set.seed(123)
  wordcloud(words = palabras_frecuentes$word, freq = palabras_frecuentes$n, min.freq = 2,
            max.words = 100, random.order = FALSE, rot.per = 0.35, 
            colors = colores_gris_naranja)
}

analizar_y_wordcloud(form_satisfaccion, "molestias_puesto")
## # A tibble: 2 × 2
##   sentiment     n
##   <chr>     <int>
## 1 trust         3
## 2 negative      2

¿Qué factor ayuda a que el personal dure más tiempo dentro de la empresa?

# Sin Información Suficiente

¿En qué parte de la empresa el trabajador se siente más inconforme y cuál es su perfil?

# Generar los datos para la variable 'permanencia_form_futuro'
datos_permanencia <- form_satisfaccion %>%
  group_by(permanencia_form_futuro) %>%
  summarise(Count = n()) %>%
  mutate(Percentage = Count / sum(Count) * 100)

# Paleta de colores personalizada: rojo, dorado, verde
colores_personalizados <- c("#d7191c", "#fdae61", "#ffffbf", "#a6d96a", "#1a9641")

# Gráfico de barras para 'permanencia_form_futuro'
ggplot(datos_permanencia, aes(x = factor(permanencia_form_futuro, levels = c(1, 2, 3, 4, 5)), y = Percentage, fill = as.factor(permanencia_form_futuro))) +
  geom_bar(stat = "identity") +
  scale_fill_manual(values = colores_personalizados) +
  scale_x_discrete(labels = c("Totalmente\nen desacuerdo", "Medianamente\nen desacuerdo", "Ni de acuerdo\nni en desacuerdo", "Medianamente\nde acuerdo", "Totalmente\nde acuerdo")) +
  xlab("Nivel de Acuerdo con la Permanencia a Futuro") +
  ylab("Porcentaje") +
  ggtitle("Distribución de Respuestas para 'Permanencia a Futuro'") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 0, hjust = 0.5, size = 8), 
        legend.title = element_blank(),
        legend.position = "bottom")

# Filtrar para obtener solo aquellos en desacuerdo con la permanencia a futuro (valor 1 o 2)
desacuerdo_permanencia <- form_satisfaccion %>% 
  filter(permanencia_form_futuro %in% c(1, 2))

# Selecciona solo las columnas de interés para el boxplot
columnas_interes <- c("salario_bueno", "prestaciones_bueno", "jornada_no_excesiva", 
                      "ofrecimiento_herramientas", "no_molestia_temperatura", "estres_bajo", 
                      "facilidad_transporte", "zona_trabajo_comoda")

# Convertir datos a formato largo para boxplots
datos_largos <- desacuerdo_permanencia %>%
  select(all_of(columnas_interes)) %>%
  pivot_longer(
    cols = everything(), 
    names_to = "Variable", 
    values_to = "Puntuacion"
  )

# Generar boxplot para cada variable seleccionada
ggplot(datos_largos, aes(x = Variable, y = Puntuacion, fill = Variable)) +
  geom_boxplot() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), axis.title.x = element_blank()) +
  labs(title = "Distribución de Puntuaciones para Empleados en Desacuerdo con la Permanencia", 
       y = "Puntuación") +
  scale_fill_brewer(palette = "Set3")  # Usar una paleta de colores adecuada para distinguir variables

form_satisfaccion$edad <- as.numeric(as.character(form_satisfaccion$edad))
desacuerdo_permanencia <- form_satisfaccion %>%
  filter(permanencia_form_futuro %in% c(1, 2))

# Diagrama de dispersión para ver la relación entre 'edad' y 'personas_dependientes'
ggplot(form_satisfaccion, aes(x = edad, y = personas_dependientes)) +
  geom_point(color = "lightgrey") +
  geom_point(data = desacuerdo_permanencia, aes(x = edad, y = personas_dependientes), alpha = 0.6, color = "blue") +
  theme_minimal() +
  scale_x_continuous() + # Asumimos que la edad es numérica y ya no hay etiquetas específicas
  ggtitle("Comparación de Edad y Personas Dependientes (Filtrado por Poca Permanencia)") +
  xlab("Edad") +
  ylab("Personas Dependientes")

# Filtrar para obtener solo aquellos en desacuerdo con la permanencia a futuro (valor 1 o 2)
desacuerdo_permanencia <- form_satisfaccion %>% 
  filter(permanencia_form_futuro %in% c(1, 2))

# Obtener el conteo de cada razón de entrada y ordenarlo de mayor a menor
conteo_razon <- desacuerdo_permanencia %>%
  count(razon_entrada) %>%
  arrange(desc(n))

# Crear el gráfico de barras horizontal con barra más grande arriba
ggplot(conteo_razon, aes(x = reorder(razon_entrada, n), y = n, fill = n)) +
  geom_bar(stat = "identity") +
  scale_fill_gradient(low = "lightblue", high = "darkblue") +
  labs(y = "Razón de Entrada", x = "Conteo", title = "Conteo de Razón de Entrada para Desacuerdo con Permanencia") +
  theme_minimal() +
  theme(axis.text.y = element_text(hjust = 1),
        legend.position = "none") +
  coord_flip()  # Girar el gráfico para orientación horizontal

Situación Problema 2: Predicción de Demanda

¿Cuáles son los patrones históricos de demanda de empaques de cartón para autopartes en los mercados objetivo de la empresa FORM, y cómo varían estos patrones según la temporada, el tipo de autoparte, y las regiones geográficas?

# Agrupar las ventas por cuarto
ventas_por_quarter <- ventas_vehiculos %>%
  group_by(Fecha) %>%
  summarise(VEH_HIBRIDAS = sum(VEH_HIBRIDAS), .groups = 'drop')

# Gráfico para exportaciones
p1 <- ggplot(exp_vehiculos, aes(x = Fecha, y = trade_value)) +
  geom_line(color = "blue") +
  labs(title = "Exportaciones por Cuarto", y = "Valor de Exportaciones") +
  theme_minimal()

# Gráfico para ventas de vehículos híbridos
p2 <- ggplot(ventas_por_quarter, aes(x = Fecha, y = VEH_HIBRIDAS)) +
  geom_line(color = "green") +
  labs(title = "Ventas de Vehículos", y = "Número de Vehículos") +
  theme_minimal()

# Arreglar los gráficos lado a lado para comparar visualmente
p1 + p2 + plot_layout(guides = 'collect') & theme(legend.position = "bottom")
## Warning: The `trans` argument of `continuous_scale()` is deprecated as of ggplot2 3.5.0.
## ℹ Please use the `transform` argument instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

# Preparar los datos para descomposición
exp_ts <- ts(exp_vehiculos$trade_value, frequency = 4) # Frecuencia trimestral
ventas_ts <- ts(ventas_por_quarter$VEH_HIBRIDAS, frequency = 4) # Frecuencia trimestral

# Descomposición STL de las exportaciones
exp_decomp <- stl(exp_ts, s.window = "periodic")
plot(exp_decomp)

# Descomposición STL de las ventas de vehículos híbridos
ventas_decomp <- stl(ventas_ts, s.window = "periodic")
plot(ventas_decomp)

⁠¿Cómo impactan los lanzamientos de nuevos modelos de vehículos y las tendencias en la industria automotriz en la demanda de diferentes tipos de empaques de cartón para autopartes?

mx_vehicle_exports_summary <- exp_vehiculos_total %>%
  group_by(Año, Segmento) %>%
  summarise(Cantidad_Total = sum(Cantidad, na.rm = TRUE))

# Definir la paleta de colores personalizada
colores <- c("#2D3250", "#435585", "#7077A1", "#F6B17A", "#9EC8B9", "#BE3144")

# Asegurarse de que hay suficientes colores para los segmentos
# Repetir la paleta si hay más segmentos que colores
n_segmentos <- length(unique(mx_vehicle_exports_summary$Segmento))
if (n_segmentos > length(colores)) {
  colores <- rep(colores, length.out = n_segmentos)
}

# Gráfico de líneas con la paleta de colores personalizada
ggplot(mx_vehicle_exports_summary, aes(x = Año, y = Cantidad_Total, color = Segmento, group = Segmento)) + 
  geom_line() + # Dibuja líneas
  geom_point() + # Añade puntos en cada dato
  scale_color_manual(values = colores) + # Usa la paleta de colores definida
  theme_minimal() + # Usa un tema minimalista
  labs(title = "Evolución de la Exportación de Segmento de Vehículo a lo Largo de los Años",
       x = "Año",
       y = "Cantidad Total") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

⁠¿Cuál ha sido el efecto de las campañas de marketing y las actividades promocionales de la empresa FORM sobre la demanda de sus productos en el pasado?

# Sin Información Suficiente

⁠¿Cómo impactan algunas variables externas de carácter macroeconómico a las ventas de la empresa?

# Sin Información Suficiente
LS0tCnRpdGxlOiAiUHJvYmxlbSBTaXR1YXRpb24gQW5hbHlzaXMiCmF1dGhvcjogIkRhdmlkIERvbWluZ3VleiAtIEEwMTU3MDk3NSIKZGF0ZTogIjIwMjQtMDMtMjEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0aGVtZTogY29zbW8KLS0tCgohW0ZPUk0gQkFOTkVSXSgvVXNlcnMvZGF2aWRkcnVtczE4MC9UZWMvQ2FzZV9TdHVkeV9Gb3JtL3NyYy9mb3JtLmpwZykKCiMjIExpYnJlcsOtYXMgbmVjZXNhcmlhcwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGltcHV0ZVRTKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeSh4dHMpCmxpYnJhcnkoem9vKQpsaWJyYXJ5KHRzZXJpZXMpCmxpYnJhcnkoc3RhdHMpCmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkoYXN0c2EpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkod29yZGNsb3VkKQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KEFFUikKbGlicmFyeSh2YXJzKQpsaWJyYXJ5KGR5bmxtKQpsaWJyYXJ5KG1GaWx0ZXIpCmxpYnJhcnkoVFNzdHVkaW8pCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNhcmltYSkKbGlicmFyeShyZWFkcikKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGhlYXRtYXBseSkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBzeWNoKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHJlYWR0ZXh0KQpsaWJyYXJ5KHN5dXpoZXQpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KHRtKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkocnBhcnQucGxvdCkKbGlicmFyeShwYXJ0eSkKbGlicmFyeShnbW9kZWxzKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGNsdXN0ZXIpICAgIApsaWJyYXJ5KGUxMDcxKQpsaWJyYXJ5KGphbmVhdXN0ZW5yKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoSVNMUikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KERhdGFFeHBsb3JlcikKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoY2xhc3MpCmxpYnJhcnkoZmFjdG9leHRyYSkKbGlicmFyeShwdXJycikKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeSh0bWFwKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KHpvbykKYGBgCgojIyBCYXNlcyBkZSBEYXRvcyBOZWNlc2FyaWFzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNldHdkKCIuLi9kYXRhYmFzZXMiKQoKZm9ybV9zYXRpc2ZhY2Npb24gPC0gcmVhZF9leGNlbCgiZm9ybS9FbmN1ZXN0YV9EYXRvc19GT1JNX0ZhbGwyMDIzLnhsc3giKQpmb3JtX2JhamFzID0gcmVhZF94bHN4KCJmb3JtL3RlbXBvcmFyeS9CRERfRk9STV9CQUpBUy0yMDIzLnhsc3giKQpjcF9ubCA8LSBzdF9yZWFkKCJnZW9fcmVmZXJlbmNlX19ubC9DUF9OTCIpCgpleHBfdmVoaWN1bG9zID0gcmVhZF9jc3YoImluZHVzdHJ5X2F1dG9zX214L2V4cG9ydGFjaW9uX3ZlaGljdWxvc19teC5jc3YiKQp2ZW50YXNfdmVoaWN1bG9zID0gcmVhZF9jc3YoImluZHVzdHJ5X2F1dG9zX214L214X3ZlbnRhX3ZlaGljdWxvcy5jc3YiKQpleHBfdmVoaWN1bG9zX3RvdGFsID0gcmVhZF9leGNlbCgiaW5kdXN0cnlfYXV0b3NfbXgvZXhwb3J0YWNpb25fdmVoaWN1bG9zLnhsc3giKQpgYGAKCiMjIFRyYW5zZm9ybWFjaW9uZXMgTmVjZXNhcmlhcwoKIyMjIEZPUk0gQmFqYXMKYGBge3J9CiMgTGltcGllemEgZGUgRmVjaGEKZm9ybV9iYWphcyRgRmVjaGEgZGUgTmFjaW1pZW50b2AgPC0gYXMuRGF0ZShmb3JtX2JhamFzJGBGZWNoYSBkZSBOYWNpbWllbnRvYCAtIGlmZWxzZShmb3JtX2JhamFzJGBGZWNoYSBkZSBOYWNpbWllbnRvYCA+IDYwLCAxLCAwKSwgb3JpZ2luPSIxODk5LTEyLTMwIikKZm9ybV9iYWphcyRgRmVjaGEgZGUgQWx0YWAgPC0gYXMuRGF0ZShmb3JtX2JhamFzJGBGZWNoYSBkZSBBbHRhYCAtIGlmZWxzZShmb3JtX2JhamFzJGBGZWNoYSBkZSBBbHRhYCA+IDYwLCAxLCAwKSwgb3JpZ2luPSIxODk5LTEyLTMwIikKZm9ybV9iYWphcyRgUHJpbWVyIE1lc2AgPC0gYXMuRGF0ZShmb3JtX2JhamFzJGBQcmltZXIgTWVzYCAtIGlmZWxzZShmb3JtX2JhamFzJGBQcmltZXIgTWVzYCA+IDYwLCAxLCAwKSwgb3JpZ2luPSIxODk5LTEyLTMwIikKZm9ybV9iYWphcyRgQ3VhcnRvIE1lc2AgPC0gYXMuRGF0ZShmb3JtX2JhamFzJGBDdWFydG8gTWVzYCAtIGlmZWxzZShmb3JtX2JhamFzJGBDdWFydG8gTWVzYCA+IDYwLCAxLCAwKSwgb3JpZ2luPSIxODk5LTEyLTMwIikKZm9ybV9iYWphcyRgRmVjaGEgZGUgQmFqYWAgPC0gYXMuRGF0ZShmb3JtX2JhamFzJGBGZWNoYSBkZSBCYWphYCAtIGlmZWxzZShmb3JtX2JhamFzJGBGZWNoYSBkZSBCYWphYCA+IDYwLCAxLCAwKSwgb3JpZ2luPSIxODk5LTEyLTMwIikKCiMgQ2FsY3VsYXIgbGEgYW50aWfDvGVkYWQgY29tbyBsYSBkaWZlcmVuY2lhIGVudHJlIEZlY2hhIGRlIEJhamEgeSBGZWNoYSBkZSBBbHRhCmZvcm1fYmFqYXMkQW50aWd1ZWRhZCA8LSBhYnMoYXMubnVtZXJpYyhmb3JtX2JhamFzJGBGZWNoYSBkZSBCYWphYCAtIGZvcm1fYmFqYXMkYEZlY2hhIGRlIEFsdGFgLCB1bml0cyA9ICJkYXlzIikpCgpoZWFkKGZvcm1fYmFqYXMpCmBgYAoKIyMjIEZPUk0gQmFqYXMgRXNwYWNpYWwKYGBge3J9CmZvcm1fYmFqYXMkQ1AgPC0gYXMuY2hhcmFjdGVyKGZvcm1fYmFqYXMkQ1ApCmNwX25sJGRfY29kaWdvIDwtIGFzLmNoYXJhY3RlcihjcF9ubCRkX2NvZGlnbykKCnN0YXRzX3Bvcl9jcCA8LSBmb3JtX2JhamFzICU+JQogIGdyb3VwX2J5KENQKSAlPiUKICBzdW1tYXJpemUoCiAgICBDb250ZW9fRW1wbGVhZG9zID0gbigpLAogICAgTWVkaWFuYV9BbnRpZ3VlZGFkID0gbWVkaWFuKEFudGlndWVkYWQsIG5hLnJtID0gVFJVRSksCiAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgKQoKZm9ybV9iYWphc19lc3BhY2lhbCA8LSBjcF9ubCAlPiUKICBsZWZ0X2pvaW4oc3RhdHNfcG9yX2NwLCBieSA9IGMoImRfY29kaWdvIiA9ICJDUCIpKQoKIyBSZWVtcGxhemFyIE5BIGNvbiAwIGVuIGxhcyBjb2x1bW5hcyBkZSBpbnRlcsOpcwpmb3JtX2JhamFzX2VzcGFjaWFsJENvbnRlb19FbXBsZWFkb3NbaXMubmEoZm9ybV9iYWphc19lc3BhY2lhbCRDb250ZW9fRW1wbGVhZG9zKV0gPC0gMApmb3JtX2JhamFzX2VzcGFjaWFsJE1lZGlhbmFfQW50aWd1ZWRhZFtpcy5uYShmb3JtX2JhamFzX2VzcGFjaWFsJE1lZGlhbmFfQW50aWd1ZWRhZCldIDwtIDAKCmhlYWQoZm9ybV9iYWphc19lc3BhY2lhbCkKYGBgCgojIyMgRk9STSBTYXRpc2ZhY2Npw7NuCmBgYHtyfQojIExpc3RhIGRlIGNvbHVtbmFzIGVzcGVjw61maWNhcyBhIHRyYW5zZm9ybWFyCmNvbHVtbmFzX2FfdHJhbnNmb3JtYXIgPC0gYygic2FsYXJpb19idWVubyIsICJwcmVzdGFjaW9uZXNfYnVlbm8iLCAiam9ybmFkYV9ub19leGNlc2l2YSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9mcmVjaW1pZW50b19oZXJyYW1pZW50YXMiLCAibm9fbW9sZXN0aWFfdGVtcGVyYXR1cmEiLCAiZXN0cmVzX2Jham8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmYWNpbGlkYWRfdHJhbnNwb3J0ZSIsICJ6b25hX3RyYWJham9fY29tb2RhIiwgInBlcm1hbmVuY2lhX2Zvcm1fZnV0dXJvIikKCiMgQWp1c3RhciBsYSBmdW5jacOzbiBwYXJhIG1hbmVqYXIgZWwgdHlwbyB5IHZhbG9yZXMgaW5lc3BlcmFkb3MKY29kaWZpY2FyX3Jlc3B1ZXN0YXMgPC0gZnVuY3Rpb24ocmVzcHVlc3RhKSB7CiAgIyBDb3JyZWdpciBwb3NpYmxlcyB0eXBvcwogIHJlc3B1ZXN0YSA8LSBnc3ViKCJUb3RhbG1lbmRlIGVuIGRlc2FjdWVyZG8iLCAiVG90YWxtZW50ZSBlbiBkZXNhY3VlcmRvIiwgcmVzcHVlc3RhKQogIAogICMgVXNhciBzd2l0Y2ggcGFyYSBhc2lnbmFyIHZhbG9yZXMgbnVtw6lyaWNvcwogIHN3aXRjaChyZXNwdWVzdGEsCiAgICAgICAgICJUb3RhbG1lbnRlIGVuIGRlc2FjdWVyZG8iID0gMSwKICAgICAgICAgIk1lZGlhbmFtZW50ZSBlbiBkZXNhY3VlcmRvIiA9IDIsCiAgICAgICAgICJOaSBkZSBhY3VlcmRvIG5pIGVuIGRlc2FjdWVyZG8iID0gMywKICAgICAgICAgIk1lZGlhbmFtZW50ZSBkZSBhY3VlcmRvIiA9IDQsCiAgICAgICAgICJUb3RhbG1lbnRlIGRlIGFjdWVyZG8iID0gNSwKICAgICAgICAgTkEpICMgRGV2b2x2ZXIgTkEgcGFyYSBjdWFscXVpZXIgcmVzcHVlc3RhIG5vIHJlY29ub2NpZGEKfQoKIyBBcGxpY2FyIGxhIGNvZGlmaWNhY2nDs24gc29sbyBhIGxhcyBjb2x1bW5hcyBzZWxlY2Npb25hZGFzLCBhc2VndXJhbmRvIHF1ZSB0b2RhcyBsYXMgdHJhbnNmb3JtYWNpb25lcyBzb24gbnVtw6lyaWNhcwpmb3JtX3NhdGlzZmFjY2lvbltjb2x1bW5hc19hX3RyYW5zZm9ybWFyXSA8LSBsYXBwbHkoZm9ybV9zYXRpc2ZhY2Npb25bY29sdW1uYXNfYV90cmFuc2Zvcm1hcl0sIGZ1bmN0aW9uKHgpIGFzLm51bWVyaWMoc2FwcGx5KHgsIGNvZGlmaWNhcl9yZXNwdWVzdGFzKSkpCgpoZWFkKGZvcm1fc2F0aXNmYWNjaW9uKQpgYGAKCiMjIyBFeHBvcnRhY2nDs24gZGUgVmVow61jdWxvcyBNw6l4aWNvCmBgYHtyfQpleHBfdmVoaWN1bG9zJEZlY2hhIDwtIGFzLnllYXJxdHIoZXhwX3ZlaGljdWxvcyRxdWFydGVyLCBmb3JtYXQgPSAiJVktUSVxIikKYGBgCgojIyMgVmVudGEgZGUgVmVow61jdWxvcyBNw6l4aWNvCmBgYHtyfQp2ZW50YXNfdmVoaWN1bG9zJEZlY2hhIDwtIGFzLnllYXJxdHIocGFzdGUodmVudGFzX3ZlaGljdWxvcyRBTklPLCB2ZW50YXNfdmVoaWN1bG9zJElEX01FUywgIjEiKSwgZm9ybWF0ID0gIiVZICVtICVkIikKYGBgCgoKIyMgUHJlZ3VudGFzIGRlIEFuw6FsaXNpcwoKIyMjICoqU2l0dWFjacOzbiBQcm9ibGVtYSAxOiBSb3RhY2nDs24gZGUgUGVyc29uYWwqKgoKIyMjIyDCv1F1w6kgZXN0w6EgY2F1c2FuZG8gbGEgcm90YWNpw7NuIGRlIHBlcnNvbmFsIHRhbiBhbHRhPwpgYGB7cn0KIyBHZW5lcmFyIGxvcyBkYXRvcyBwYXJhIGxhIHZhcmlhYmxlICdub19tb2xlc3RpYV90ZW1wZXJhdHVyYScKZGF0b3NfdGVtcGVyYXR1cmEgPC0gZm9ybV9zYXRpc2ZhY2Npb24gJT4lCiAgZ3JvdXBfYnkobm9fbW9sZXN0aWFfdGVtcGVyYXR1cmEpICU+JQogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lCiAgbXV0YXRlKFBlcmNlbnRhZ2UgPSBDb3VudCAvIHN1bShDb3VudCkgKiAxMDApCgojIFBhbGV0YSBkZSBjb2xvcmVzIHBlcnNvbmFsaXphZGE6IHJvam8sIGRvcmFkbywgdmVyZGUKY29sb3Jlc19wZXJzb25hbGl6YWRvcyA8LSBjKCIjZDcxOTFjIiwgIiNmZGFlNjEiLCAiI2ZmZmZiZiIsICIjYTZkOTZhIiwgIiMxYTk2NDEiKQoKIyBHcsOhZmljbyBkZSBiYXJyYXMgcGFyYSAnbm9fbW9sZXN0aWFfdGVtcGVyYXR1cmEnCmdncGxvdChkYXRvc190ZW1wZXJhdHVyYSwgYWVzKHggPSBmYWN0b3Iobm9fbW9sZXN0aWFfdGVtcGVyYXR1cmEsIGxldmVscyA9IGMoMSwgMiwgMywgNCwgNSkpLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IGFzLmZhY3Rvcihub19tb2xlc3RpYV90ZW1wZXJhdHVyYSkpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjb2xvcmVzX3BlcnNvbmFsaXphZG9zKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCJUb3RhbG1lbnRlXG5lbiBkZXNhY3VlcmRvIiwgIk1lZGlhbmFtZW50ZVxuZW4gZGVzYWN1ZXJkbyIsICJOaSBkZSBhY3VlcmRvXG5uaSBlbiBkZXNhY3VlcmRvIiwgIk1lZGlhbmFtZW50ZVxuZGUgYWN1ZXJkbyIsICJUb3RhbG1lbnRlXG5kZSBhY3VlcmRvIikpICsKICB4bGFiKCJOaXZlbCBkZSBBY3VlcmRvIGNvbiBsYSBUZW1wZXJhdHVyYSIpICsKICB5bGFiKCJQb3JjZW50YWplIikgKwogIGdndGl0bGUoIkRpc3RyaWJ1Y2nDs24gZGUgUmVzcHVlc3RhcyBwYXJhICdObyBNb2xlc3RpYSBwb3IgbGEgVGVtcGVyYXR1cmEnIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDAuNSwgc2l6ZSA9IDgpLCAKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKYGBge3J9CnZhcmlhYmxlc19jYXRlZ29yaWNhcyA8LSBjKCJzYWxhcmlvX2J1ZW5vIiwgInByZXN0YWNpb25lc19idWVubyIsICJqb3JuYWRhX25vX2V4Y2VzaXZhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvZnJlY2ltaWVudG9faGVycmFtaWVudGFzIiwgIm5vX21vbGVzdGlhX3RlbXBlcmF0dXJhIiwgImVzdHJlc19iYWpvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJmYWNpbGlkYWRfdHJhbnNwb3J0ZSIsICJ6b25hX3RyYWJham9fY29tb2RhIiwgInBlcm1hbmVuY2lhX2Zvcm1fZnV0dXJvIikKCiMgQ2FsY3VsYW1vcyBsb3MgcG9yY2VudGFqZXMgcG9yIGNhdGVnb3LDrWEgcGFyYSBjYWRhIHZhcmlhYmxlCnBvcmNlbnRhamVzX2NhdGVnb3JpYXMgPC0gZm9ybV9zYXRpc2ZhY2Npb24gJT4lCiAgc2VsZWN0KGFsbF9vZih2YXJpYWJsZXNfY2F0ZWdvcmljYXMpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiVmFyaWFibGUiLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKSAlPiUKICBncm91cF9ieShWYXJpYWJsZSkgJT4lCiAgY291bnQoVmFsb3IpICU+JQogIG11dGF0ZShQb3JjZW50YWplID0gbiAvIHN1bShuKSAqIDEwMCkgJT4lCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdCgtbikgICMgU2kgbm8gbmVjZXNpdGFzIGxhIGNvbHVtbmEgZGUgY29udGVvcywgcHVlZGVzIG9taXRpcmxhLgoKIyBDb252ZXJ0aXIgYSB1biBmb3JtYXRvIGFuY2hvIGVzcGVjw61maWNvIHBhcmEgY2FkYSB2YXJpYWJsZSB5IHZhbG9yCnBvcmNlbnRhamVzX2FuY2hvIDwtIHBvcmNlbnRhamVzX2NhdGVnb3JpYXMgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFZhbG9yLCB2YWx1ZXNfZnJvbSA9IFBvcmNlbnRhamUsIG5hbWVzX3ByZWZpeCA9ICJDYXRlZ29yaWFfIikgJT4lCiAgc2VsZWN0KFZhcmlhYmxlLCBzdGFydHNfd2l0aCgiQ2F0ZWdvcmlhXyIpKSAlPiUKICBhcnJhbmdlKFZhcmlhYmxlKQoKIyBNb2RpZmljYW1vcyAncG9yY2VudGFqZXNfYW5jaG8nIHBhcmEgZm9ybWF0ZWFyIGxvcyBwb3JjZW50YWplcwpwb3JjZW50YWplc19hbmNobyA8LSBwb3JjZW50YWplc19hbmNobyAlPiUKICBtdXRhdGUoYWNyb3NzKHN0YXJ0c193aXRoKCJDYXRlZ29yaWFfIiksIH5zcHJpbnRmKCIlLjFmJSUiLCAuKSkpCgojIFZlciBsb3MgcmVzdWx0YWRvcwpwcmludChwb3JjZW50YWplc19hbmNobykKYGBgCgoKIyMjIyDCv0V4aXN0ZSBhbGfDum4gbW9tZW50byBkZWwgYcOxbyBkb25kZSBzZSBkZW4gbcOhcyBiYWphcyBkZW50cm8gZGUgbGEgZW1wcmVzYT8KCmBgYHtyfQojIEFncmVnYXIgdW5hIGNvbHVtbmEgcXVlIGluZGlxdWUgZWwgbWVzIGRlIGxhIGZlY2hhIGRlIGJhamEKZm9ybV9iYWphcyRNZXMgPC0gbW9udGgoZm9ybV9iYWphcyRgRmVjaGEgZGUgQmFqYWApCmZvcm1fYmFqYXMkQcOxbyA8LSB5ZWFyKGZvcm1fYmFqYXMkYEZlY2hhIGRlIEJhamFgKQoKIyBBZ3J1cGFyIHBvciBtZXMgeSBnw6luZXJvLCBsdWVnbyBjb250YXIgbGFzIGJhamFzCmJhamFzX3Bvcl9tZXNfZ2VuZXJvIDwtIGZvcm1fYmFqYXMgJT4lCiAgZ3JvdXBfYnkoTWVzLCBHw6luZXJvKSAlPiUKICBzdW1tYXJpc2UoQ29udGVvID0gbigpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUKICBhcnJhbmdlKE1lcykKCiMgR3JhZmljYXIgbGFzIGJhamFzIHBvciBtZXMgcGFyYSBjYWRhIGfDqW5lcm8KZ2dwbG90KGJhamFzX3Bvcl9tZXNfZ2VuZXJvLCBhZXMoeCA9IE1lcywgeSA9IENvbnRlbywgZ3JvdXAgPSBHw6luZXJvLCBjb2xvciA9IEfDqW5lcm8pKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjEyLCBsYWJlbHMgPSBtb250aC5uYW1lKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIkJhamFzIHBvciBNZXMgRGl2aWRpZG8gcG9yIEfDqW5lcm8iLAogICAgICAgeCA9ICJNZXMiLAogICAgICAgeSA9ICJOw7ptZXJvIGRlIEJhamFzIiwKICAgICAgIGNvbG9yID0gIkfDqW5lcm8iKQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIENvbmZpZ3VyYXIgdG1hcCBwYXJhIHF1ZSBpbnRlbnRlIHJlcGFyYXIgYXV0b23DoXRpY2FtZW50ZSBsb3MgcG9sw61nb25vcyBpbnbDoWxpZG9zCnRtYXBfb3B0aW9ucyhjaGVjay5hbmQuZml4ID0gVFJVRSkKCiMgQ2FsY3VsYXIgbG9zIGJyZWFrcyBwYXJhIENvbnRlbyBkZSBFbXBsZWFkb3MgZXhjbHV5ZW5kbyBsb3MgY2Vyb3MKY29udGVvX2VtcGxlYWRvc192YWx1ZXMgPC0gZm9ybV9iYWphc19lc3BhY2lhbCRDb250ZW9fRW1wbGVhZG9zW2Zvcm1fYmFqYXNfZXNwYWNpYWwkQ29udGVvX0VtcGxlYWRvcyA+IDBdCmJyZWFrc19jb250ZW8gPC0gYygtSW5mLCBxdWFudGlsZShjb250ZW9fZW1wbGVhZG9zX3ZhbHVlcywgcHJvYnMgPSBzZXEoMCwgMSwgYnkgPSAwLjI1KSwgbmEucm0gPSBUUlVFKSkKCiMgQ2FsY3VsYXIgbG9zIGJyZWFrcyBwYXJhIE1lZGlhbmEgZGUgQW50aWfDvGVkYWQgZXhjbHV5ZW5kbyBsb3MgY2Vyb3MKYW50aWd1ZWRhZF92YWx1ZXMgPC0gZm9ybV9iYWphc19lc3BhY2lhbCRNZWRpYW5hX0FudGlndWVkYWRbZm9ybV9iYWphc19lc3BhY2lhbCRNZWRpYW5hX0FudGlndWVkYWQgPiAwXQpicmVha3NfYW50aWd1ZWRhZCA8LSBjKC1JbmYsIHF1YW50aWxlKGFudGlndWVkYWRfdmFsdWVzLCBwcm9icyA9IHNlcSgwLCAxLCBieSA9IDAuMjUpLCBuYS5ybSA9IFRSVUUpKQoKIyBNYXBhIHBhcmEgQ29udGVvIGRlIEVtcGxlYWRvcwpjb250ZW9fZW1wbGVhZG9zX21hcCA8LSB0bV9zaGFwZShmb3JtX2JhamFzX2VzcGFjaWFsKSArIAogIHRtX2ZpbGwoIkNvbnRlb19FbXBsZWFkb3MiLCBwYWxldHRlID0gIkJsdWVzIiwgc3R5bGUgPSAiZml4ZWQiLCBicmVha3MgPSBicmVha3NfY29udGVvLCAKICAgICAgICAgIHRpdGxlID0gIkNvbnRlbyBkZSBFbXBsZWFkb3MiLCBuYS5jb2xvciA9ICJ3aGl0ZSIpICsKICB0bV9ib3JkZXJzKCkgKwogIHRtX2xheW91dChmcmFtZSA9IEZBTFNFLCBsZWdlbmQucG9zaXRpb24gPSBjKCJsZWZ0IiwgImJvdHRvbSIpKQoKIyBNYXBhIHBhcmEgTWVkaWFuYSBkZSBBbnRpZ8O8ZWRhZAptZWRpYW5hX2FudGlndWVkYWRfbWFwIDwtIHRtX3NoYXBlKGZvcm1fYmFqYXNfZXNwYWNpYWwpICsgCiAgdG1fZmlsbCgiTWVkaWFuYV9BbnRpZ3VlZGFkIiwgcGFsZXR0ZSA9ICJCdVB1Iiwgc3R5bGUgPSAiZml4ZWQiLCBicmVha3MgPSBicmVha3NfYW50aWd1ZWRhZCwgCiAgICAgICAgICB0aXRsZSA9ICJNZWRpYW5hIGRlIEFudGlnw7xlZGFkIiwgbmEuY29sb3IgPSAid2hpdGUiKSArCiAgdG1fYm9yZGVycygpICsKICB0bV9sYXlvdXQoZnJhbWUgPSBGQUxTRSwgbGVnZW5kLnBvc2l0aW9uID0gYygicmlnaHQiLCAiYm90dG9tIikpCgojIENvbmZpZ3VyYXIgb3BjaW9uZXMgZGUgdG1hcCBwYXJhIGxhIHZpc3VhbGl6YWNpw7NuIGRlIGxvcyBtYXBhcwp0bWFwX21vZGUoInZpZXciKQoKIyBNb3N0cmFyIG1hcGFzIGxhZG8gYSBsYWRvCnRtYXBfYXJyYW5nZShjb250ZW9fZW1wbGVhZG9zX21hcCwgbWVkaWFuYV9hbnRpZ3VlZGFkX21hcCwgbnJvdyA9IDEpCmBgYAoKCiMjIyMgwr9Dw7NtbyBzZSBzaWVudGUgbGEgZ2VudGUgZGVudHJvIGRlIHN1IHRyYWJham8gYWN0dWFsPwpgYGB7cn0KIyBGdW5jacOzbiBhanVzdGFkYSBwYXJhIHByZXByb2Nlc2FyIHRleHRvCnByZXBhcmFyX3RleHRvIDwtIGZ1bmN0aW9uKGRhdG9zLCBjb2x1bW5hKSB7CiAgZGF0b3MgJT4lCiAgICBzZWxlY3QoISFzeW0oY29sdW1uYSkpICU+JQogICAgbXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIGFzLmNoYXJhY3RlcikpICU+JQogICAgbXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIH4gZ3N1YigiXFxkKyIsICIiLCAuKSkpICU+JQogICAgdW5uZXN0X3Rva2Vucyh3b3JkLCAhIXN5bShjb2x1bW5hKSkKfQoKIyBGdW5jacOzbiBhanVzdGFkYSBwYXJhIGFuw6FsaXNpcyBkZSBzZW50aW1pZW50byB5IHdvcmRjbG91ZCBjb24gY29sb3JlcyBwZXJzb25hbGl6YWRvcwphbmFsaXphcl95X3dvcmRjbG91ZCA8LSBmdW5jdGlvbihkYXRvcywgY29sdW1uYSkgewogIHRleHRvX3ByZXBhcmFkbyA8LSBwcmVwYXJhcl90ZXh0byhkYXRvcywgY29sdW1uYSkKICAKICAjIE9idGVuZXIgc2VudGltaWVudG8KICBlbW9jaW9uZXNfZGYgPC0gdGV4dG9fcHJlcGFyYWRvICU+JQogICAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIiksIGJ5ID0gIndvcmQiKSAlPiUKICAgIGNvdW50KHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JQogICAgcHJpbnQoKQogIAogICMgRmlsdHJhciBwYWxhYnJhcyBwYXJhIGVsIHdvcmRjbG91ZCAoYXBhcmljacOzbiBtw61uaW1hOiAzIHZlY2VzKQogIHBhbGFicmFzX2ZyZWN1ZW50ZXMgPC0gdGV4dG9fcHJlcGFyYWRvICU+JQogICAgY291bnQod29yZCwgc29ydCA9IFRSVUUpICU+JQogICAgZmlsdGVyKG4gPj0gMykKICAKICAjIERlZmluaXIgcGFsZXRhIGRlIGNvbG9yZXMgZGUgZ3JpcyBjbGFybyBhIG5hcmFuamEKICBjb2xvcmVzX2dyaXNfbmFyYW5qYSA8LSBjb2xvclJhbXBQYWxldHRlKGMoImRhcmtncmV5IiwgIm9yYW5nZSIpKSgxMDApCiAgCiAgIyBHZW5lcmFyIHdvcmRjbG91ZCBjb24gbGEgcGFsZXRhIHBlcnNvbmFsaXphZGEKICBzZXQuc2VlZCgxMjMpCiAgd29yZGNsb3VkKHdvcmRzID0gcGFsYWJyYXNfZnJlY3VlbnRlcyR3b3JkLCBmcmVxID0gcGFsYWJyYXNfZnJlY3VlbnRlcyRuLCBtaW4uZnJlcSA9IDIsCiAgICAgICAgICAgIG1heC53b3JkcyA9IDEwMCwgcmFuZG9tLm9yZGVyID0gRkFMU0UsIHJvdC5wZXIgPSAwLjM1LCAKICAgICAgICAgICAgY29sb3JzID0gY29sb3Jlc19ncmlzX25hcmFuamEpCn0KCmFuYWxpemFyX3lfd29yZGNsb3VkKGZvcm1fc2F0aXNmYWNjaW9uLCAibW9sZXN0aWFzX3B1ZXN0byIpCmBgYAoKIyMjIyDCv1F1w6kgZmFjdG9yIGF5dWRhIGEgcXVlIGVsIHBlcnNvbmFsIGR1cmUgbcOhcyB0aWVtcG8gZGVudHJvIGRlIGxhIGVtcHJlc2E/CgpgYGB7cn0KIyBTaW4gSW5mb3JtYWNpw7NuIFN1ZmljaWVudGUKYGBgCgojIyMjIMK/RW4gcXXDqSBwYXJ0ZSBkZSBsYSBlbXByZXNhIGVsIHRyYWJhamFkb3Igc2Ugc2llbnRlIG3DoXMgaW5jb25mb3JtZSB5IGN1w6FsIGVzIHN1IHBlcmZpbD8KCmBgYHtyfQojIEdlbmVyYXIgbG9zIGRhdG9zIHBhcmEgbGEgdmFyaWFibGUgJ3Blcm1hbmVuY2lhX2Zvcm1fZnV0dXJvJwpkYXRvc19wZXJtYW5lbmNpYSA8LSBmb3JtX3NhdGlzZmFjY2lvbiAlPiUKICBncm91cF9ieShwZXJtYW5lbmNpYV9mb3JtX2Z1dHVybykgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbigpKSAlPiUKICBtdXRhdGUoUGVyY2VudGFnZSA9IENvdW50IC8gc3VtKENvdW50KSAqIDEwMCkKCiMgUGFsZXRhIGRlIGNvbG9yZXMgcGVyc29uYWxpemFkYTogcm9qbywgZG9yYWRvLCB2ZXJkZQpjb2xvcmVzX3BlcnNvbmFsaXphZG9zIDwtIGMoIiNkNzE5MWMiLCAiI2ZkYWU2MSIsICIjZmZmZmJmIiwgIiNhNmQ5NmEiLCAiIzFhOTY0MSIpCgojIEdyw6FmaWNvIGRlIGJhcnJhcyBwYXJhICdwZXJtYW5lbmNpYV9mb3JtX2Z1dHVybycKZ2dwbG90KGRhdG9zX3Blcm1hbmVuY2lhLCBhZXMoeCA9IGZhY3RvcihwZXJtYW5lbmNpYV9mb3JtX2Z1dHVybywgbGV2ZWxzID0gYygxLCAyLCAzLCA0LCA1KSksIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gYXMuZmFjdG9yKHBlcm1hbmVuY2lhX2Zvcm1fZnV0dXJvKSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yZXNfcGVyc29uYWxpemFkb3MpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoIlRvdGFsbWVudGVcbmVuIGRlc2FjdWVyZG8iLCAiTWVkaWFuYW1lbnRlXG5lbiBkZXNhY3VlcmRvIiwgIk5pIGRlIGFjdWVyZG9cbm5pIGVuIGRlc2FjdWVyZG8iLCAiTWVkaWFuYW1lbnRlXG5kZSBhY3VlcmRvIiwgIlRvdGFsbWVudGVcbmRlIGFjdWVyZG8iKSkgKwogIHhsYWIoIk5pdmVsIGRlIEFjdWVyZG8gY29uIGxhIFBlcm1hbmVuY2lhIGEgRnV0dXJvIikgKwogIHlsYWIoIlBvcmNlbnRhamUiKSArCiAgZ2d0aXRsZSgiRGlzdHJpYnVjacOzbiBkZSBSZXNwdWVzdGFzIHBhcmEgJ1Blcm1hbmVuY2lhIGEgRnV0dXJvJyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAwLjUsIHNpemUgPSA4KSwgCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCmBgYHtyfQojIEZpbHRyYXIgcGFyYSBvYnRlbmVyIHNvbG8gYXF1ZWxsb3MgZW4gZGVzYWN1ZXJkbyBjb24gbGEgcGVybWFuZW5jaWEgYSBmdXR1cm8gKHZhbG9yIDEgbyAyKQpkZXNhY3VlcmRvX3Blcm1hbmVuY2lhIDwtIGZvcm1fc2F0aXNmYWNjaW9uICU+JSAKICBmaWx0ZXIocGVybWFuZW5jaWFfZm9ybV9mdXR1cm8gJWluJSBjKDEsIDIpKQoKIyBTZWxlY2Npb25hIHNvbG8gbGFzIGNvbHVtbmFzIGRlIGludGVyw6lzIHBhcmEgZWwgYm94cGxvdApjb2x1bW5hc19pbnRlcmVzIDwtIGMoInNhbGFyaW9fYnVlbm8iLCAicHJlc3RhY2lvbmVzX2J1ZW5vIiwgImpvcm5hZGFfbm9fZXhjZXNpdmEiLCAKICAgICAgICAgICAgICAgICAgICAgICJvZnJlY2ltaWVudG9faGVycmFtaWVudGFzIiwgIm5vX21vbGVzdGlhX3RlbXBlcmF0dXJhIiwgImVzdHJlc19iYWpvIiwgCiAgICAgICAgICAgICAgICAgICAgICAiZmFjaWxpZGFkX3RyYW5zcG9ydGUiLCAiem9uYV90cmFiYWpvX2NvbW9kYSIpCgojIENvbnZlcnRpciBkYXRvcyBhIGZvcm1hdG8gbGFyZ28gcGFyYSBib3hwbG90cwpkYXRvc19sYXJnb3MgPC0gZGVzYWN1ZXJkb19wZXJtYW5lbmNpYSAlPiUKICBzZWxlY3QoYWxsX29mKGNvbHVtbmFzX2ludGVyZXMpKSAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBjb2xzID0gZXZlcnl0aGluZygpLCAKICAgIG5hbWVzX3RvID0gIlZhcmlhYmxlIiwgCiAgICB2YWx1ZXNfdG8gPSAiUHVudHVhY2lvbiIKICApCgojIEdlbmVyYXIgYm94cGxvdCBwYXJhIGNhZGEgdmFyaWFibGUgc2VsZWNjaW9uYWRhCmdncGxvdChkYXRvc19sYXJnb3MsIGFlcyh4ID0gVmFyaWFibGUsIHkgPSBQdW50dWFjaW9uLCBmaWxsID0gVmFyaWFibGUpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnVjacOzbiBkZSBQdW50dWFjaW9uZXMgcGFyYSBFbXBsZWFkb3MgZW4gRGVzYWN1ZXJkbyBjb24gbGEgUGVybWFuZW5jaWEiLCAKICAgICAgIHkgPSAiUHVudHVhY2nDs24iKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikgICMgVXNhciB1bmEgcGFsZXRhIGRlIGNvbG9yZXMgYWRlY3VhZGEgcGFyYSBkaXN0aW5ndWlyIHZhcmlhYmxlcwpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmZvcm1fc2F0aXNmYWNjaW9uJGVkYWQgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZm9ybV9zYXRpc2ZhY2Npb24kZWRhZCkpCmRlc2FjdWVyZG9fcGVybWFuZW5jaWEgPC0gZm9ybV9zYXRpc2ZhY2Npb24gJT4lCiAgZmlsdGVyKHBlcm1hbmVuY2lhX2Zvcm1fZnV0dXJvICVpbiUgYygxLCAyKSkKCiMgRGlhZ3JhbWEgZGUgZGlzcGVyc2nDs24gcGFyYSB2ZXIgbGEgcmVsYWNpw7NuIGVudHJlICdlZGFkJyB5ICdwZXJzb25hc19kZXBlbmRpZW50ZXMnCmdncGxvdChmb3JtX3NhdGlzZmFjY2lvbiwgYWVzKHggPSBlZGFkLCB5ID0gcGVyc29uYXNfZGVwZW5kaWVudGVzKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAibGlnaHRncmV5IikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGRlc2FjdWVyZG9fcGVybWFuZW5jaWEsIGFlcyh4ID0gZWRhZCwgeSA9IHBlcnNvbmFzX2RlcGVuZGllbnRlcyksIGFscGhhID0gMC42LCBjb2xvciA9ICJibHVlIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKCkgKyAjIEFzdW1pbW9zIHF1ZSBsYSBlZGFkIGVzIG51bcOpcmljYSB5IHlhIG5vIGhheSBldGlxdWV0YXMgZXNwZWPDrWZpY2FzCiAgZ2d0aXRsZSgiQ29tcGFyYWNpw7NuIGRlIEVkYWQgeSBQZXJzb25hcyBEZXBlbmRpZW50ZXMgKEZpbHRyYWRvIHBvciBQb2NhIFBlcm1hbmVuY2lhKSIpICsKICB4bGFiKCJFZGFkIikgKwogIHlsYWIoIlBlcnNvbmFzIERlcGVuZGllbnRlcyIpCmBgYAoKYGBge3J9CiMgRmlsdHJhciBwYXJhIG9idGVuZXIgc29sbyBhcXVlbGxvcyBlbiBkZXNhY3VlcmRvIGNvbiBsYSBwZXJtYW5lbmNpYSBhIGZ1dHVybyAodmFsb3IgMSBvIDIpCmRlc2FjdWVyZG9fcGVybWFuZW5jaWEgPC0gZm9ybV9zYXRpc2ZhY2Npb24gJT4lIAogIGZpbHRlcihwZXJtYW5lbmNpYV9mb3JtX2Z1dHVybyAlaW4lIGMoMSwgMikpCgojIE9idGVuZXIgZWwgY29udGVvIGRlIGNhZGEgcmF6w7NuIGRlIGVudHJhZGEgeSBvcmRlbmFybG8gZGUgbWF5b3IgYSBtZW5vcgpjb250ZW9fcmF6b24gPC0gZGVzYWN1ZXJkb19wZXJtYW5lbmNpYSAlPiUKICBjb3VudChyYXpvbl9lbnRyYWRhKSAlPiUKICBhcnJhbmdlKGRlc2MobikpCgojIENyZWFyIGVsIGdyw6FmaWNvIGRlIGJhcnJhcyBob3Jpem9udGFsIGNvbiBiYXJyYSBtw6FzIGdyYW5kZSBhcnJpYmEKZ2dwbG90KGNvbnRlb19yYXpvbiwgYWVzKHggPSByZW9yZGVyKHJhem9uX2VudHJhZGEsIG4pLCB5ID0gbiwgZmlsbCA9IG4pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJsaWdodGJsdWUiLCBoaWdoID0gImRhcmtibHVlIikgKwogIGxhYnMoeSA9ICJSYXrDs24gZGUgRW50cmFkYSIsIHggPSAiQ29udGVvIiwgdGl0bGUgPSAiQ29udGVvIGRlIFJhesOzbiBkZSBFbnRyYWRhIHBhcmEgRGVzYWN1ZXJkbyBjb24gUGVybWFuZW5jaWEiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGNvb3JkX2ZsaXAoKSAgIyBHaXJhciBlbCBncsOhZmljbyBwYXJhIG9yaWVudGFjacOzbiBob3Jpem9udGFsCmBgYAoKCiMjIyAqKlNpdHVhY2nDs24gUHJvYmxlbWEgMjogUHJlZGljY2nDs24gZGUgRGVtYW5kYSoqCgojIyMjIMK/Q3XDoWxlcyBzb24gbG9zIHBhdHJvbmVzIGhpc3TDs3JpY29zIGRlIGRlbWFuZGEgZGUgZW1wYXF1ZXMgZGUgY2FydMOzbiBwYXJhIGF1dG9wYXJ0ZXMgZW4gbG9zIG1lcmNhZG9zIG9iamV0aXZvIGRlIGxhIGVtcHJlc2EgRk9STSwgeSBjw7NtbyB2YXLDrWFuIGVzdG9zIHBhdHJvbmVzIHNlZ8O6biBsYSB0ZW1wb3JhZGEsIGVsIHRpcG8gZGUgYXV0b3BhcnRlLCB5IGxhcyByZWdpb25lcyBnZW9ncsOhZmljYXM/CgpgYGB7cn0KIyBBZ3J1cGFyIGxhcyB2ZW50YXMgcG9yIGN1YXJ0bwp2ZW50YXNfcG9yX3F1YXJ0ZXIgPC0gdmVudGFzX3ZlaGljdWxvcyAlPiUKICBncm91cF9ieShGZWNoYSkgJT4lCiAgc3VtbWFyaXNlKFZFSF9ISUJSSURBUyA9IHN1bShWRUhfSElCUklEQVMpLCAuZ3JvdXBzID0gJ2Ryb3AnKQoKIyBHcsOhZmljbyBwYXJhIGV4cG9ydGFjaW9uZXMKcDEgPC0gZ2dwbG90KGV4cF92ZWhpY3Vsb3MsIGFlcyh4ID0gRmVjaGEsIHkgPSB0cmFkZV92YWx1ZSkpICsKICBnZW9tX2xpbmUoY29sb3IgPSAiYmx1ZSIpICsKICBsYWJzKHRpdGxlID0gIkV4cG9ydGFjaW9uZXMgcG9yIEN1YXJ0byIsIHkgPSAiVmFsb3IgZGUgRXhwb3J0YWNpb25lcyIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgR3LDoWZpY28gcGFyYSB2ZW50YXMgZGUgdmVow61jdWxvcyBow61icmlkb3MKcDIgPC0gZ2dwbG90KHZlbnRhc19wb3JfcXVhcnRlciwgYWVzKHggPSBGZWNoYSwgeSA9IFZFSF9ISUJSSURBUykpICsKICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JlZW4iKSArCiAgbGFicyh0aXRsZSA9ICJWZW50YXMgZGUgVmVow61jdWxvcyIsIHkgPSAiTsO6bWVybyBkZSBWZWjDrWN1bG9zIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBBcnJlZ2xhciBsb3MgZ3LDoWZpY29zIGxhZG8gYSBsYWRvIHBhcmEgY29tcGFyYXIgdmlzdWFsbWVudGUKcDEgKyBwMiArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICdjb2xsZWN0JykgJiB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgpgYGB7cn0KIyBQcmVwYXJhciBsb3MgZGF0b3MgcGFyYSBkZXNjb21wb3NpY2nDs24KZXhwX3RzIDwtIHRzKGV4cF92ZWhpY3Vsb3MkdHJhZGVfdmFsdWUsIGZyZXF1ZW5jeSA9IDQpICMgRnJlY3VlbmNpYSB0cmltZXN0cmFsCnZlbnRhc190cyA8LSB0cyh2ZW50YXNfcG9yX3F1YXJ0ZXIkVkVIX0hJQlJJREFTLCBmcmVxdWVuY3kgPSA0KSAjIEZyZWN1ZW5jaWEgdHJpbWVzdHJhbAoKIyBEZXNjb21wb3NpY2nDs24gU1RMIGRlIGxhcyBleHBvcnRhY2lvbmVzCmV4cF9kZWNvbXAgPC0gc3RsKGV4cF90cywgcy53aW5kb3cgPSAicGVyaW9kaWMiKQpwbG90KGV4cF9kZWNvbXApCgojIERlc2NvbXBvc2ljacOzbiBTVEwgZGUgbGFzIHZlbnRhcyBkZSB2ZWjDrWN1bG9zIGjDrWJyaWRvcwp2ZW50YXNfZGVjb21wIDwtIHN0bCh2ZW50YXNfdHMsIHMud2luZG93ID0gInBlcmlvZGljIikKcGxvdCh2ZW50YXNfZGVjb21wKQpgYGAKCiMjIyMg4oGgwr9Dw7NtbyBpbXBhY3RhbiBsb3MgbGFuemFtaWVudG9zIGRlIG51ZXZvcyBtb2RlbG9zIGRlIHZlaMOtY3Vsb3MgeSBsYXMgdGVuZGVuY2lhcyBlbiBsYSBpbmR1c3RyaWEgYXV0b21vdHJpeiBlbiBsYSBkZW1hbmRhIGRlIGRpZmVyZW50ZXMgdGlwb3MgZGUgZW1wYXF1ZXMgZGUgY2FydMOzbiBwYXJhIGF1dG9wYXJ0ZXM/CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpteF92ZWhpY2xlX2V4cG9ydHNfc3VtbWFyeSA8LSBleHBfdmVoaWN1bG9zX3RvdGFsICU+JQogIGdyb3VwX2J5KEHDsW8sIFNlZ21lbnRvKSAlPiUKICBzdW1tYXJpc2UoQ2FudGlkYWRfVG90YWwgPSBzdW0oQ2FudGlkYWQsIG5hLnJtID0gVFJVRSkpCgojIERlZmluaXIgbGEgcGFsZXRhIGRlIGNvbG9yZXMgcGVyc29uYWxpemFkYQpjb2xvcmVzIDwtIGMoIiMyRDMyNTAiLCAiIzQzNTU4NSIsICIjNzA3N0ExIiwgIiNGNkIxN0EiLCAiIzlFQzhCOSIsICIjQkUzMTQ0IikKCiMgQXNlZ3VyYXJzZSBkZSBxdWUgaGF5IHN1ZmljaWVudGVzIGNvbG9yZXMgcGFyYSBsb3Mgc2VnbWVudG9zCiMgUmVwZXRpciBsYSBwYWxldGEgc2kgaGF5IG3DoXMgc2VnbWVudG9zIHF1ZSBjb2xvcmVzCm5fc2VnbWVudG9zIDwtIGxlbmd0aCh1bmlxdWUobXhfdmVoaWNsZV9leHBvcnRzX3N1bW1hcnkkU2VnbWVudG8pKQppZiAobl9zZWdtZW50b3MgPiBsZW5ndGgoY29sb3JlcykpIHsKICBjb2xvcmVzIDwtIHJlcChjb2xvcmVzLCBsZW5ndGgub3V0ID0gbl9zZWdtZW50b3MpCn0KCiMgR3LDoWZpY28gZGUgbMOtbmVhcyBjb24gbGEgcGFsZXRhIGRlIGNvbG9yZXMgcGVyc29uYWxpemFkYQpnZ3Bsb3QobXhfdmVoaWNsZV9leHBvcnRzX3N1bW1hcnksIGFlcyh4ID0gQcOxbywgeSA9IENhbnRpZGFkX1RvdGFsLCBjb2xvciA9IFNlZ21lbnRvLCBncm91cCA9IFNlZ21lbnRvKSkgKyAKICBnZW9tX2xpbmUoKSArICMgRGlidWphIGzDrW5lYXMKICBnZW9tX3BvaW50KCkgKyAjIEHDsWFkZSBwdW50b3MgZW4gY2FkYSBkYXRvCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9yZXMpICsgIyBVc2EgbGEgcGFsZXRhIGRlIGNvbG9yZXMgZGVmaW5pZGEKICB0aGVtZV9taW5pbWFsKCkgKyAjIFVzYSB1biB0ZW1hIG1pbmltYWxpc3RhCiAgbGFicyh0aXRsZSA9ICJFdm9sdWNpw7NuIGRlIGxhIEV4cG9ydGFjacOzbiBkZSBTZWdtZW50byBkZSBWZWjDrWN1bG8gYSBsbyBMYXJnbyBkZSBsb3MgQcOxb3MiLAogICAgICAgeCA9ICJBw7FvIiwKICAgICAgIHkgPSAiQ2FudGlkYWQgVG90YWwiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCgojIyMjIOKBoMK/Q3XDoWwgaGEgc2lkbyBlbCBlZmVjdG8gZGUgbGFzIGNhbXBhw7FhcyBkZSBtYXJrZXRpbmcgeSBsYXMgYWN0aXZpZGFkZXMgcHJvbW9jaW9uYWxlcyBkZSBsYSBlbXByZXNhIEZPUk0gc29icmUgbGEgZGVtYW5kYSBkZSBzdXMgcHJvZHVjdG9zIGVuIGVsIHBhc2Fkbz8KCmBgYHtyfQojIFNpbiBJbmZvcm1hY2nDs24gU3VmaWNpZW50ZQpgYGAKCiMjIyMg4oGgwr9Dw7NtbyBpbXBhY3RhbiBhbGd1bmFzIHZhcmlhYmxlcyBleHRlcm5hcyBkZSBjYXLDoWN0ZXIgbWFjcm9lY29uw7NtaWNvIGEgbGFzIHZlbnRhcyBkZSBsYSBlbXByZXNhPwoKYGBge3J9CiMgU2luIEluZm9ybWFjacOzbiBTdWZpY2llbnRlCmBgYAoK